#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

u"""
pyLot widget interface.
  - 03-2010, Pierre Puiseux, Guillaume Baty
  
Université de Pau et des Pays de l'Adour, INRIA
"""

__all__ = [u'Widget']

from Qt4 import QtGui, QtCore

from traits.api import implements

from pyLot.application import APPLICATION
from pyLot.core import Path, LANG, Handler, check_type

from _pylot.qtgui.tools import (set_qt_widget_texts, get_icon, fill)
from _pylot.qtgui import install_qtranslator

from .impl_basewidget import BaseWidget
from ..interfaces.i_widget import IWidget

class Widget(BaseWidget):
  implements(IWidget)

  default_handler_class = Handler
  default_handler_kargs = {}

  tool_bars = property(fget=lambda self:self._tool_bars)
  docks = property(fget=lambda self:self._docks)
  menus = property(fget=lambda self:self._menus)
  pref = property(fget=lambda self:self._pref)
  handler = property(fget=lambda self:self._handler)
  pm = property(fget=lambda self:APPLICATION.getPluginManager())

  def __init__(self, handler=None):
    u"""
    :param handler: un handler associé au widget. 
          Le widget traite de l'interface, le handler traite des données.
    :type handler: HandlerInterface (?)
           
    """
    self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

    self.__translators = {}
    self.__current_locale = None

    self._tool_bars = []
    self._menus = []
    self._widgets = []
    self._docks = []
    self._pref = None

    self._plugin_path = Path(u'.').abspath()

    if handler is None :
      handler = self.default_handler_class(**self.default_handler_kargs)

    check_type(u'handler', handler, self.default_handler_class)
    self._handler = handler

  # ============================================================================
  # Methods to implement
  # ============================================================================

  def _init(self):
    u"""
    Use this method to finish widget initialization.    
    For example, loading last file.
    
    Default: do nothing
    """

  def _readyToClose(self):
    return True

  def _switchUserLevel(self, level):
    pass

  def _fillToolBars(self, level):
    u"""
    Clears and fills toolbars depending on level.
    
    Example ::
      self._fill(self.tool_bar_pylot, [
        self.action_all_levels,
        (self.action_expert, u'expert')
        ])
    """

  def _fillMenus(self, level):
    u"""
    Clears and fills menus depending on level.
    
    Example ::
      self._fill(self.menu_pylot, [
        self.action_all_levels,
        (self.action_expert, u'expert')
        ])
    """

  def _readSettings(self, qsettings):
    pass

  def _writeSettings(self, qsettings):
    pass

  def _translate (self) :
    pass

  def _updateData(self):
    pass

  def _refresh(self):
    pass

  def _decorate (self) :
    pass


  # ============================================================================
  # Widget Creation
  # ============================================================================

  def _fill(self, widget, actions, clear=True):
    u"""
    Fills widget with actions.
    Widget must be a QToolBar, a QMenuBar or QMenu.
    "actions" is a list of actions.

    Available type for actions are :
      - simple action: QAction or QMenu
      - couple (simple action, level) (see switchUserLevel)
      - u'-' (separator)
      
    :param widget: widget you want to fill
    :type widget: see description
    :param actions: list of actions
    :type widget: obj:`list`, see description
    """
    fill(widget, actions, self.handler.level, clear=clear)

  # ============================================================================
  # Adding plugins, menus, ...
  # ============================================================================

  def addMenu_(self, menu, name=None):
    u"""
    Adds QMenu "menu" to widget.
    Adding menu with this method allows automatic treatments like menu 
    generation.

    See also
    :meth:`~_pylot.qtgui.interfaces.widgetinterface.WidgetInterface.removeMenu_`
    """
    if isinstance(name, basestring) :
      menu.setObjectName(name)
    if menu not in self.menus :
      self.menus.append(menu)
    return menu

  def removeMenu_(self, menu):
    u"""
    Removes menu. 
    
    See also
    :meth:`~_pylot.qtgui.interfaces.widgetinterface.WidgetInterface.addMenu_`
    """
    if menu in self.menus :
      self.menus.remove(menu)

  def addToolBar_(self, tool_bar):
    u"""
    Adds QToolBar "tool_bar" to widget.
    Adding menu with this method allows automatic treatments like adding all
    tool_bars to mainwindow.

    See also
    :meth:`~_pylot.qtgui.interfaces.widgetinterface.WidgetInterface.removeToolBar_`
    """
    if tool_bar not in self.tool_bars :
      self.tool_bars.append(tool_bar)
    return tool_bar

  def removeToolBar_(self, tool_bar):
    u"""
    Remove tool_bar.
    See also
    :meth:`~_pylot.qtgui.interfaces.widgetinterface.WidgetInterface.addToolBar_`
    """
    if tool_bar in self.tool_bars :
      self.tool_bars.remove(tool_bar)

  def append(self, widget):
    if widget not in self :
      self._widgets.append(widget)

  def __iter__(self):
    for widget in self._widgets :
      yield widget

  def _createActions(self):
    u"""
    Les actions du widget ...
    Actions are typed of QAction
    
    .. warning ::
      Action may be defined here or in designer file.
      Texts and titles must be defined in _translate methods, don't forget to do it !

    Good example::
      self.action_quit = QAction()
    
    Wrong example::
      self.action_quit = QAction(u'Quit')
    """

  def _createMenus(self):
    u"""
    Creates empty QMenu(s). This method must be called in widget __init__
  
    L'affichage du menu est assuré par l'appelant.
    Le widget UiWidgetInterface n'affiche rien.
    
    This method must fill self.menus dict (see constructor)

    .. warning ::
      Texts and titles must be defined in _translate methods, don't forget to do it !
      You must set an object name, else it may cause problems.

    Good example ::  
      self.menu_xyz = QMenu()
      menu.setObjectName(u'MenuXyz')
      self.addMenu_(self.menu_xyz)

    Wrong example ::
      self.menu_xyz = QMenu(u'Menu')
    """

  def _createToolBars(self):
    u"""
    Creates empty QToolBar(s). This method must be called in widget __init__
  
    L'affichage de la tool_bar est assuré par l'appelant.
    Le widget UiWidgetInterface n'affiche rien.

    .. warning ::
      Texts and titles must be defined in _translate methods, don't forget do it !
      You must set an object name, else it may cause problems and tool_bar state
      will not be saved.

    Example ::
  
      self.tool_bar_pylot = QToolBar()
      self.tool_bar_pylot.setObjectName(u"tool_bar_pylot")
      self.tool_bar_pylot.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
      self.addToolBar_(self.tool_bar_pylot)
    """

  def _createPreferences(self):
    u"""
    Creates preferences widget

    .. note ::
      You should implement this class

    Example ::
    
      pref = GuiPrefWidget()
      self.pref = pref
    """


  # ============================================================================
  # Widget Settings
  # ============================================================================
  def readSettings(self, userconf_dir, recursive=True):
    u"""
    .. warning ::
      Instructions must be defined in private method _readSettings.
      Only inherits this method if you really know what you are talking about.

    :param settings: setting object
    :type settings: QSettings
    """
    for module in self :
      module.readSettings(userconf_dir, recursive)

    if self.pref :
      self.pref.readSettings(userconf_dir, recursive)


    qsettings = QtCore.QSettings(
      unicode(Path(userconf_dir, u'%s.conf' % self.identifier)))

    qsettings.sync()
    self._readSettings(qsettings)

  def writeSettings(self, userconf_dir, recursive=True):
    u"""
    .. warning ::
      Instructions must be defined in private method _writeSettings.
      Only inherits this method if you really know what you are talking about.

    :param settings: setting object where you want to write
    :type settings: QSettings
    """
    for module in self :
      module.writeSettings(userconf_dir, recursive)

    if self.pref :
      self.pref.writeSettings(userconf_dir, recursive)

    qsettings = QtCore.QSettings(
        unicode(Path(userconf_dir, u'%s.conf' % self.identifier)),
        QtCore.QSettings.NativeFormat)

    self._writeSettings(qsettings)
    qsettings.sync()

  def init(self):
    u"""
    .. warning ::
      Instructions must be defined in private method _init.
      Only inherits this method if you really know what you are talking about.
    """
    for module in self :
      QtGui.QTimer.singleShot(0, module.init)

    if self.pref :
      QtGui.QTimer.singleShot(0, self.pref.init)

    return self._init()

  def readyToClose(self):
    close = True
    for w in self:
      if w.readyToClose() is False :
        close = False

    return self._readyToClose() and close

#  def close(self, event):
#    u"""
#    .. warning ::
#      Instructions must be defined in private method _close.
#      Only inherits this method if you really know what you are talking about.
#    """
#    super(Widget, self).close(event)
#    for module in self :
#      module.close(event)
#
#    if self.pref :
#      self.pref.close(event)
#
#    return self._close(event)

  # ============================================================================
  # Widget Dynamic Changes
  # ============================================================================

  def switchUserLevel(self, level=None):
    u"""
    Switches display level.
    This method calls recursivly switchUserLevel method of all children's 
    widgets and preferences.

    If level is not defined, it uses handler level.

    .. warning ::

      Level switching instructions must be defined in following private methods:
        - _fillMenus
        - _fillToolBars
        - _switchUserLevel
  
      Only inherits this method if you really know what you are talking about.
    """
    if level :
      self.handler.switchUserLevel(level)
    level = self.handler.level

    for module in self :
      module.switchUserLevel(level)

    if self.pref :
      self.pref.switchUserLevel(level)

    self._fillMenus(level)
    self._fillToolBars(level)
    self._switchUserLevel(level)

  menus = property(fget=lambda self:self._menus)
  tr_ = property(fget=lambda self:self._handler.tr_)

  def translate(self, locale=None, force=False, recursive=True):
    u"""
    Retranslates widget only if locale has changed.
    You can force it with force=True.
    
    This method calls recursivly translate method of all children's widgets and
    preferences. You can disable recursivity with recursive=False.

    If locale is not defined, uses handler locale. if handler locale is not
    defined, uses system default locale.
    
    .. warning ::
      Translation instructions must be defined in _translate method.
      Only inherits this method if you really know what you are talking about.
    """
    if locale is None :
      locale = self.handler.current_locale
    if locale is None : #means handler has never been translated
      locale = LANG

    if locale == self.__current_locale and force is False :
      return
    self.__current_locale = locale

    # Install global QTranslator for ui files
    install_qtranslator(locale)

    # Install local QTranslator for ui files
    try :
      self._plugin_path
    except AttributeError :
      pass
    else :
      try :
        translator = self.__translators[locale]
      except KeyError :
        translator = QtCore.QTranslator()
        self.__translators[locale] = translator
      i18n_path = unicode(Path(self._plugin_path, u'i18n', u'%s.qm' % locale).abspath())
      loaded = translator.load(i18n_path)

      for old_translator in self.__translators.values():
        QtCore.QCoreApplication.instance().removeTranslator(old_translator)

      if loaded :
        QtCore.QCoreApplication.instance().installTranslator(translator)

    # translate submodules
    if recursive is True :
      for module in self :
        module.translate(locale, force=force, recursive=recursive)

    # retranslate ui file if available
    try :
      self.retranslateUi(self)
    except AttributeError :
      pass

    # retranslate widget
    self._translate()

  def updateData(self):
    u"""
    Updates core data from widget fields
    """
    self._updateData()

  def refresh(self):
    u"""
    Updates widget fields from core data
    """
    for module in self :
      module.refresh()

    self._refresh()

  def decorate(self):
    u"""
    Decorates widget (set icons, images, etc.).
    This method calls recursivly decorate method of all children's 
    widgets and preferences.

    .. warning ::
      Instructions must be defined in private method _decorate.
      Only inherits this method if you really know what you are talking about.
    """
    for module in self :
      module.decorate()

    self._decorate()

  def fineTune(self):
    for module in self :
      module.fineTune()

    self._fineTune()

  # ============================================================================
  # Tool-methods, not necessary to implement IBaseWidget but useful
  # ============================================================================

  def _setTexts(self, widget_text_list, field=u'text'):
    u"""
    Puts texts (titles, tooltips, shortcuts, ...) for a list of widgets.

    :param widget_text_list: list of couple (widget(QWidget), text(unicode))
    :type widget_text_list: obj:`list`
    :param field: type of text you want to set : tooltip, shortcut or text [default]
    :type field: obj:`unicode`
    """
    set_qt_widget_texts(widget_text_list, field)

  def _setIcons(self, widget_icon_list):
    u"""
    Finds and puts icons (from filename) for a list of widgets.

    :param widget_icon_list: list of couple (widget(QWidget), filename(unicode))
    :type widget_icon_list: obj:`list`
    """
    for widget, path in widget_icon_list :
      widget.setIcon(self._icon(path))

  def _icon (self, filename):
    u"""
    Tries to find image file "filename" from icon directories and generates a
    QIcon object from this path.
    If fails, searches in current working directory or return an empty QIcon.
    
    :param filename: picture filename
    :type filename: unicode
    :rtype: QtGui.QIcon
    """
    if isinstance(filename, QtGui.QIcon) :
      return filename
    try :
      icon = get_icon(filename, [Path(self._plugin_path, u'icons')])
    except AttributeError :
      icon = get_icon(filename)
    return icon

  def _createConnexions(self):
    u"""
    Connexions des actions/signaux aux méthodes.

    .. note ::
      You should implement this class    

    Example ::
      self.connect(self.action_quit, SIGNAL("triggered()"), exit)
    """

  def createTestBox(self):
    from _pylot.qtgui.widgets.gui_testbox import GuiTestBox
    box = GuiTestBox(self)
    box.show()
    return box

if  __name__ == u"__main__":
  pass
